//File:	Collada.cpp
#include "Collada.h"
#include <GL\glew.h>
#include <GL\glfw.h>
#include <list>
#include <vector>
#include "Utility.h"
#include "E_Resource.h"

using namespace std;

// |---------- class Node ----------|
Node::Node() 
{
	Create();
}
Node::Node(std::string _Name)
{
	Create(_Name);
}

void Node::Create(std::string _Name)
{
	Name = _Name;
	Parent = NULL;
	Nodes = std::list<Node>(0);
	Keys = std::vector<Key>(0);
	Parent = NULL;
	Next = NULL;
	Prev = NULL;
}

Node* Node::RequestNode()
{
	Node *SecondLast = NULL;
	if(Nodes.size() > 0)
	{
		SecondLast = &Nodes.back();
	}

	Nodes.push_back(Node());
	Node *RequestedNode = &Nodes.back();
	RequestedNode->Parent = this;
	RequestedNode->Next = NULL;


	if (SecondLast != NULL)
	{
		SecondLast->Next = RequestedNode;
		RequestedNode->Prev = SecondLast;
	}

	return RequestedNode;
}

void Node::AddKey(std::string Name,std::string Value)
{
	Keys.push_back(Key(Name,Value));
}
void Node::AddKey(Key key)
{
	Keys.push_back(key);
}

Node* Node::GetNode(std::string Name, int ithMatch)
{
	for (auto it = Nodes.begin();it != Nodes.end();it++)
	{
		if ( CompareString(it->Name,Name) )
		{
			if (ithMatch == 1)
			{
				return &(*it);
			}
			else
			{
				ithMatch -= 1;
			}
		}
		else
		{
			Utility::Log("Node: Query Failed, no immediate nodes named "+Name);
			return NULL;
		}
	}
	Utility::Log("Node: Query Failed, no immediate nodes named "+Name);
	return NULL;
}
Node* Node::GetAnyNode(std::string Name, bool Subprocess)
{
	for (auto it = Nodes.begin();it != Nodes.end();it++)
	{
		if ( CompareString(it->Name,Name) )
		{
			return &(*it);
		}


		Node* Find = it->GetAnyNode(Name,true);
		if (Find != NULL)
		{
			return Find;
		}
	}

	// Prevent spitting out errors if the method started itself
	if (Subprocess = false)
	{
		Utility::Log("Node: Query Failed, no child nodes named "+Name);
	}
	return NULL;
}

Node::Key* Node::GetKey(std::string Name)
{
	for (auto it = Keys.begin();it != Keys.end();it++)
	{
		if (CompareString(it->Name,Name))
		{
			return &(*it);
		}
		else
		{
			std::cout << "Attribute Query Failed: could not find " << Name.c_str() <<"\n";
			return NULL;
		}
	}
	return NULL;
}
Node::Key* Node::GetAnyKey(std::string Name, bool Subprocess)
{
	for (auto it = Nodes.begin();it != Nodes.end();it++)
	{				
		for (auto k = it->Keys.begin(); k != it->Keys.end(); k++)
		{
			if ( CompareString(k->Name,Name) )
			{
				return (Key*) &(*k);
			}
		}

		Key* Find = (Key*) it->GetAnyKey(Name,true);
		if (Find != NULL)
		{
			return Find;
		}
	}

	// Prevent spitting out errors if the method started itself
	if (Subprocess = false)
	{
		std::cout << "Attribute Any Query Failed: No child keys named "<<Name.c_str() <<std::endl;
	}
	return NULL;
}

bool Node::CompareString(std::string Str1,std::string Str2)
{
	Str1 = Utility::ToUpper(Str1);
	Str2 = Utility::ToUpper(Str2);

	return  Str1.compare(Str2) == 0 ? true : false;
}

void Node::MapKeys()
{
	for ( auto it = Keys.begin(); it != Keys.end(); it++)
	{
		std::cout << "Key: " << it->Name << std::endl;
		std::cout << "Value: "<< it->Value << std::endl;
	}
}
void Node::MapNodes()
{
	for ( auto it = Nodes.begin(); it != Nodes.end(); it++)
	{
		std::cout << "---Node---\n";
		std::cout << "Name: " << it->Name << std::endl;

		it->MapKeys();
		it->MapNodes();
		std::cout <<"\n";
	}
}
void Node::PrintMap()
{
	std::cout << "---Start Node---\n"<< "Name: " <<Name << std::endl;
	MapKeys();
	std::cout <<"\n";
	MapNodes();
}



// |---------- class Collada ----------|
Collada::Collada(void) {}

void Collada::ParseKeys(XMLElement* XML, Node* N)
{		

	if (XML != NULL)
	{
		if (XML->GetText() != NULL)
		{
			N->AddKey("Text",(char*) XML->GetText());
		}

		tinyxml2::XMLAttribute* Attrib = (tinyxml2::XMLAttribute*) XML->FirstAttribute();

		N->Name = (char*) XML->Name();
		while (Attrib != NULL)
		{
			N->AddKey((char*)Attrib->Name(),(char*)Attrib->Value());
			Attrib = (tinyxml2::XMLAttribute*) Attrib->Next();
		}
	}
}
void Collada::ParseSibling(XMLElement* XML, Node* Parent)
{
	if (XML != NULL)
	{
		XML = XML->FirstChildElement();

		while(XML != NULL)
		{
			Node *N = Parent->RequestNode();
			ParseKeys(XML,N);
			ParseSibling(XML,N);

			//std::cout << "Completed Node Processing: "<<N->Name<<std::endl;
			XML = XML->NextSiblingElement();
		}
	}

}

Node Collada::Load(std::string Path)
{
	XMLDocument Doc;
	if (Doc.LoadFile(Path.c_str()) == XML_SUCCESS)
	{
		Utility::Log("Collada: Loadeded ["+Path+"] successfully.");
	}
	else
	{
		Utility::Log("Collada: Failed to open file ["+Path+"]");
	}

	XMLElement *XMLMain = Doc.RootElement();

	Node Root;
	ParseKeys(XMLMain,&Root);
	ParseSibling(XMLMain,&Root);


	//Root.PrintMap();

	return Root;
}
Model Collada::LoadModel(string path)
{

	Node Root = Collada::Load(Resource::AliasAdd(Path::Models,path));
	Model ReturnModel;

	Mesh M;	
	Node *Source = Root.GetAnyNode("source");

	M.Position = Utility::Vector_SplitString<GLfloat>((char *)Source->GetAnyKey("text")->Value.c_str());
	//Source = Source->Next;
	//M.Normal = Utility::Vector_SplitString<GLfloat>((char *)Source->GetAnyKey("text")->Value.c_str());
	//Source = Source->Next;
	//M.Texcoord = Utility::Vector_SplitString<GLfloat>((char *)Source->GetAnyKey("text")->Value.c_str());


	vector<GLushort> P, N, T,FinalIndex;
	vector<GLushort> Temp = Utility::Vector_SplitString<GLushort>((char *)Root.GetAnyNode("triangles")->GetAnyNode("p")->GetKey("text")->Value.c_str());;


	int FillSize = Utility::ToInt(Root.GetAnyNode("triangles")->GetKey("count")->Value);
	for (unsigned int i = 0; i < Temp.size()/3; i++)
	{
		P.push_back(Temp[i*3]);		
		N.push_back(Temp[(i*3)+1]);		
		T.push_back(Temp[(i*3)+2]);
	}


	/*
	FinalIndex = Utility::AddVector<GLushort>(FinalIndex,P);
	FinalIndex = Utility::AddVector<GLushort>(FinalIndex,N);
	FinalIndex = Utility::AddVector<GLushort>(FinalIndex,T);


	M.Indices = FinalIndex;

	*/

	// OR...


	Utility::PrintVector(M.Position);
	Utility::PrintVector(P);
	for (int i = 0; i < FillSize; i++)
	{
		GLfloat X,Y,Z;
		X = M.Position[P[i]*3];
		Y = M.Position[(P[i]*3)+1];
		Z = M.Position[(P[i]*3)+2];

		SimpleVertex Vert(X,Y,Z);
		M.m_Interleaved.push_back(Vert);
		Vert.Print();
	}


	for (int i = 0; i < FillSize;i++)
	{
		M.Indices.push_back(i);
	}

	/*
	cout << "[Position]\n";
	Utility::PrintVector(M.Position);

	cout << endl;

	cout << "[Normal]\n";
	Utility::PrintVector(M.Normal);

	cout << endl;

	cout << "[Texcoord]\n";
	Utility::PrintVector(M.Texcoord);





	for (int i = 0; i < TempIndex.size()/3; i++)
	{
	M.Indices.push_back(TempIndex[i*3]);
	}





	cout << endl;
	for (int i = 0; i < M.Indices.size();i++)
	{
	M.Position.push_back(TempPos[M.Indices[i]*3]);
	M.Position.push_back(TempPos[M.Indices[i]*3+1]);
	M.Position.push_back(TempPos[M.Indices[i]*3+2]);
	}
	*/

	ReturnModel.m_Mesh.push_back(M);
	return ReturnModel;

}